﻿using AutoMapper;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Niaofei.Application.Contracts;
using Niaofei.Domain;
using Niaofei.Domain.Shared;
using Niaofei.Injcetion;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Text.Json;

namespace Niaofei.Application
{
    [Injection(typeof(ISysUserAppService))]
    internal class SysUserAppService : NiaofeiAppService, ISysUserAppService
    {
        private readonly AuthorizationOptions _options;
        private readonly ISysUserRepository _sysUserRepository;
        private readonly ISysUserRoleRepository _sysUserRoleRepository;
        private readonly ISysDeptRepository _sysDeptRepository;

        public SysUserAppService(IServiceProvider serviceProvider) : base(serviceProvider)
        {
            _options = serviceProvider.GetRequiredService<IOptions<AuthorizationOptions>>().Value;
            _sysUserRepository = serviceProvider.GetRequiredService<ISysUserRepository>();
            _sysUserRoleRepository = serviceProvider.GetRequiredService<ISysUserRoleRepository>();
            _sysDeptRepository = serviceProvider.GetRequiredService<ISysDeptRepository>();
        }

        public async Task<ResultDto<SysUserOutputDto>> AddUser(SysUserAddDto input)
        {
            var user = Mapper.Map<SysUserAddDto, SysUser>(input);

            user = await _sysUserRepository.InsertAsync(user);

            var userRoles = new List<SysUserRole>();
            foreach (var item in input.Roles)
            {
                var userRole = new SysUserRole()
                {
                    RoleId = item,
                    UserId = user.Id
                };

                userRoles.Add(userRole);
            }
            await _sysUserRoleRepository.InsertManyAsync(userRoles);

            var result = Mapper.Map<SysUser, SysUserOutputDto>(user);

            return new ResultDto<SysUserOutputDto>(result);
        }

        public async Task<ResultDto<SysUserOutputDto>> GetUser(long id)
        {
            var query = await _sysUserRepository.GetQueryableAsync();

            var user = query.FirstOrDefault(m => m.Id == id);

            if (user == null)
            {
                return new ResultDto<SysUserOutputDto>("未找到用户");
            }

            var roles = (await _sysUserRoleRepository.GetQueryableAsync())
                .Where(m => m.UserId == user.Id)
                .Select(m => m.RoleId).ToList();

            var result = Mapper.Map<SysUser, SysUserOutputDto>(user);

            result.Roles = roles;

            return new ResultDto<SysUserOutputDto>(result);
        }

        public async Task<ResultDto<SysUserOutputDto>> GetUser()
        {
            return await GetUser(long.Parse(CurrentUser?.Id ?? "0"));
        }

        public async Task<ResultDto<PageOutputDto<SysUserOutputDto>>> GetUserPage(SysUserInputDto input)
        {
            var query = (await _sysUserRepository.GetQueryableAsync())
                .WhereIf(!string.IsNullOrEmpty(input.KeyWord), m => m.NickName.Contains(input.KeyWord) || m.UserName.Contains(input.KeyWord))
                .WhereIf(input.DeptId.HasValue, m => m.DeptId == input.DeptId)
                .OrderByDescending(m => m.CreateTime);

            var count = query.Count();
            var list = query.Skip((input.PageNo - 1) * input.PageSize).Take(input.PageSize).ToList();

            var result = Mapper.Map<List<SysUser>, List<SysUserOutputDto>>(list);

            var deptIds = result.Select(m => m.DeptId).ToList();
            var depts = (await _sysDeptRepository.GetQueryableAsync())
                .Where(m => deptIds.Contains(m.Id)).ToList();
            foreach (var item in result)
            {
                item.DeptName = depts.FirstOrDefault(m => m.Id == item.DeptId)?.Name;
            }

            return new ResultDto<PageOutputDto<SysUserOutputDto>>(new PageOutputDto<SysUserOutputDto>(count, result));
        }

        public async Task<ResultDto<bool>> RemoveUser(long id)
        {
            var query = await _sysUserRepository.GetQueryableAsync();

            var user = query.FirstOrDefault(m => m.Id == id);

            if (user == null)
            {
                return new ResultDto<bool>() { Code = "1", Data = false, Message = "用户不存在" };
            }

            await _sysUserRepository.DeleteAsync(user, true);

            return new ResultDto<bool>(true);
        }

        public async Task<ResultDto<SysUserOutputDto>> UpdateUser(SysUserUpdateDto input)
        {
            var query = (await _sysUserRepository.GetQueryableAsync());

            var user = query.FirstOrDefault(m => m.Id == input.Id);

            if (user == null)
            {
                return new ResultDto<SysUserOutputDto>() { Code = "1", Message = "用户不存在" };
            }

            Mapper.Map(input, user);
            await _sysUserRepository.UpdateAsync(user, true);

            var roles = (await _sysUserRoleRepository.GetQueryableAsync())
                .Where(m => m.UserId == user.Id).ToList();

            var newRoles = input.Roles.Select(m =>
                new SysUserRole()
                {
                    RoleId = m,
                    UserId = user.Id
                }).ToList();
            await _sysUserRoleRepository.DeleteManyAsync(roles);
            await _sysUserRoleRepository.InsertManyAsync(newRoles);

            var result = Mapper.Map<SysUser, SysUserOutputDto>(user);

            return new ResultDto<SysUserOutputDto>(result);
        }

        public async Task<ResultDto<string>> Login(SysUserLoginInputDto input)
        {
            var user = (await _sysUserRepository.GetQueryableAsync())
                .Where(m => m.UserName == input.UserName)
                .Where(m => m.PassWord == input.PassWord)
                .FirstOrDefault();

            if (user != null)
            {
                var roles = (await _sysUserRoleRepository.GetQueryableAsync())
                    .Where(m => m.UserId == user.Id)
                    .ToList();

                var secretKey = _options.SecretKey;
                var issuer = _options.Issuer;
                var audience = _options.Audience;
                var expirationMinutes = _options.ExpirationMinutes;
                var claims = new Claim[]
                {
                    new Claim("Id", user.Id.ToString()),
                    new Claim("UserName", user.UserName),
                    new Claim("NickName", user.NickName),
                    new Claim("Email", user.Email),
                    new Claim("Gender", user.Gender.ToString()),
                    new Claim("Age", user.Age.ToString()),
                    new Claim("DeptId", user.DeptId.ToString()),
                    new Claim("Mobile", user.Mobile),
                    new Claim("Role",JsonSerializer.Serialize(roles.Select(m => m.RoleId).ToArray())),
                    new Claim("path",""),
                };

                var token = GenerateJwt(secretKey, issuer, audience, expirationMinutes, claims);

                return new ResultDto<string>() { Code = "0", Data = token };
            }

            return new ResultDto<string>() { Code = "1", Message = "账号或密码不存在!" };
        }

        public async Task<ResultDto<string>> GetUserAvatar(string userName)
        {
            var user = (await _sysUserRepository.GetQueryableAsync())
                .FirstOrDefault(m => m.UserName == userName);

            return new ResultDto<string>() { Code = "0", Data = user?.Avatar ?? "" };
        }

        private static string GenerateJwt(string secretKey, string issuer, string audience, int expirationMinutes, Claim[] claims)
        {
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: issuer,
                audience: audience,
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(expirationMinutes),
                signingCredentials: credentials
            );

            var encodedToken = new JwtSecurityTokenHandler().WriteToken(token);
            return encodedToken;
        }
    }
}
